<template>
  <div id="detail">
    <el-container class="box">
      <el-header class="headArea">
        <div @click="history = true;" style="cursor: pointer;">
          多相机视觉SLAM系统
        </div>
        <el-dropdown @command="handleCommand">
          <el-avatar style="cursor: pointer;">{{ this.userInfo }}</el-avatar>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>退出</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
        <!-- <el-avatar @click="goToLogin" style="cursor: pointer;">{{ this.userInfo }}</el-avatar> -->
      </el-header>
      <el-container class="container">
        <el-aside class="menu">
          <el-button class="btn" type="primary" @click="launch">启动SLAM</el-button>
          <el-button class="btn" type="primary" @click="setConfig">设置启动配置</el-button>
          <el-button class="btn" type="primary" @click="stop">停止SLAM</el-button>
          <el-button class="btn" type="primary" @click="lookHistory">历史轨迹记录</el-button>
        </el-aside>
        <el-main class="mainArea">
          <template v-if="history">
            <div v-if="images.length === 0">
              <el-empty
                :image-size="200"
                description="SLAM系统尚未启动"
              ></el-empty>
            </div>
            <div class="imgBox" v-for="(image, index) in images" :key="index">
              <img width="360px" :src="image" :alt="'Camera ' + index" />
              <p>Camera {{ index }}</p>
            </div>
          </template>
          <template v-else>
            <div class="fileList">
              <h1>历史轨迹列表</h1>
              <el-button type="primary" class="trajCompareBtn" @click="openTrajSetWindow">轨迹对比</el-button>
              <el-table
                ref="multipleTable"
                :data="tableData"
                border
                style="width: 100%"
                tooltip-effect="dark"
                @selection-change="handleSelectionChange">
                <el-table-column
                  type="selection"
                  width="55">
                </el-table-column>
                <el-table-column
                  fixed
                  prop="gTime"
                  label="编号"
                  width="150">
                </el-table-column>
                <el-table-column
                  prop="name"
                  label="文件名">
                </el-table-column>
                <el-table-column
                  fixed="right"
                  label="操作"
                  width="300">
                  <template slot-scope="scope">
                    <el-button @click="handleCheck(scope.row)" type="text" size="small">查看数据</el-button>
                    <el-button @click="handleTraj(scope.row)" type="text" size="small">查看轨迹</el-button>
                    <el-button @click="handleDownload(scope.row)" type="text" size="small">下载</el-button>
                    <el-button @click="handleDelete(scope.row)" type="text" size="small">删除</el-button>
                  </template>
                </el-table-column>
              </el-table>
              <el-pagination
                background
                layout="prev, pager, next"
                :total="1">
              </el-pagination>
            </div>
          </template>
        </el-main>
      </el-container>
    </el-container>

    <el-dialog title="启动参数配置" :visible.sync="dialogFormVisible">
      <el-form :model="form">
        <el-form-item label="Vocabulary File" :label-width="formLabelWidth">
          <!-- <el-input v-model="form.vocabulary" autocomplete="off" type="file" ref="fileInput" @change="handleFileChange"></el-input> -->
          <button type="button" @click="selectFile(1, 1)">选择文件</button>
          <p v-if="form.vocabulary">已选择文件：{{ form.vocabulary }}</p>
        </el-form-item>
        <el-form-item label="Settings File" :label-width="formLabelWidth">
          <!-- <el-input v-model="form.settings" autocomplete="off" type="file"></el-input> -->
          <button type="button" @click="selectFile(1, 2)">选择文件</button>
          <p v-if="form.settings">已选择文件：{{ form.settings }}</p>
          <!-- {{form.settings}} -->
        </el-form-item>
        <el-form-item label="Calibration Files" :label-width="formLabelWidth">
          <!-- <el-input v-model="form.calibration" autocomplete="off" type="file"></el-input> -->
          <button type="button" @click="selectFile(2, 3)">选择文件夹</button>
          <p v-if="form.calibration">已选择文件夹：{{ form.calibration }}</p>
          <!-- {{form.calibration}} -->
        </el-form-item>
        <el-form-item label="Images" :label-width="formLabelWidth">
          <!-- <el-input v-model="form.images" autocomplete="off" type="file"></el-input> -->
          <button type="button" @click="selectFile(2, 4)">选择文件夹</button>
          <p v-if="form.images">已选择文件夹：{{ form.images }}</p>
          <!-- {{form.images}} -->
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitForm"
          >确 定</el-button
        >
      </div>
    </el-dialog>

    <!-- 查看轨迹 -->
    <el-dialog
      :title="trajName"
      :visible.sync="trajVisible"
      width="60%">
      <el-input
        type="textarea"
        :rows="12"
        placeholder="请输入内容"
        v-model="trajText"
        readonly>
      </el-input>
      <span slot="footer" class="dialog-footer">
        <el-button @click="trajVisible = false">取 消</el-button>
        <el-button type="primary" @click="trajVisible = false">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 轨迹对比配置 -->
    <el-dialog
      :title="trajCompareName"
      :visible.sync="trajCompareVisible"
      width="60%">
        <el-form :model="trajSetForm">
          <el-form-item label="数据格式" :label-width="trajFormLabelWidth">
            <el-radio-group v-model="trajSetForm.dataFormat">
              <el-radio-button label="tum"></el-radio-button>
              <el-radio-button label="kitti"></el-radio-button>
              <el-radio-button label="euroc"></el-radio-button>
              <el-radio-button label="bag"></el-radio-button>
            </el-radio-group>          
          </el-form-item>
          <el-form-item label="绘制模式(plot_mode)" :label-width="trajFormLabelWidth">
            <el-radio-group v-model="trajSetForm.plotMode">
              <el-radio-button label="xz"></el-radio-button>
              <el-radio-button label="xy"></el-radio-button>
              <el-radio-button label="yz"></el-radio-button>
              <el-radio-button label="xyz"></el-radio-button>
            </el-radio-group>          
          </el-form-item>
          <el-form-item label="计算ape" :label-width="trajFormLabelWidth">
            <el-switch v-model="trajSetForm.computeAPE"></el-switch>
          </el-form-item>
        </el-form>


      <span slot="footer" class="dialog-footer">
        <el-button @click="trajCompareVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleCompareTraj">确 定</el-button>
      </span>
    </el-dialog>


    <!-- 显示轨迹对比结果 -->
    <el-dialog
      :title="showTrajResultName"
      :visible.sync="showTrajResult"
      width="70%">
         <div v-if="trajCompResult">
          <h3>轨迹比较结果:</h3>
          <pre>{{ trajCompResult.trajResult }}</pre>
          <img loading="lazy" v-if="trajCompResult.plotImagePath" :src="trajCompResult.plotImagePath" alt="轨迹图" style="max-width: 100%;" />

          <h3 v-if="trajCompResult.apeResult">APE 计算结果:</h3>
          <pre v-if="trajCompResult.apeResult">{{ trajCompResult.apeResult }}</pre>
          <img loading="lazy" v-if="trajCompResult.apePlotImagePath" :src="trajCompResult.apePlotImagePath" alt="APE 轨迹图" style="max-width: 80%;" />
        </div>

      <span slot="footer" class="dialog-footer">
        <el-button @click="showTrajResult = false">取 消</el-button>
        <el-button type="primary" @click="showTrajResult = false">确 定</el-button>
      </span>
    </el-dialog>

  </div>
</template>

<script>

export default {
  name: "Detail",
  components: {},
  data() {
    return {
      images: [], // 用于存储每帧接收的图像数据,
      imgSrc: [
        "/home/zc/slam/data/lafida/outdoor_large_loop/imgs/cam0/img0.png",
        "/home/zc/slam/data/lafida/outdoor_large_loop/imgs/cam0/img0.png",
        "/home/zc/slam/data/lafida/outdoor_large_loop/imgs/cam0/img0.png"
      ],
      dialogFormVisible: false,
      trajVisible: false,
      trajCompareVisible: false,
      showTrajResult: false,
      form: {
        vocabulary: "",
        settings: "",
        calibration: "",
        images: "",
        // 第一个是词汇表文件的路径。第二个是设置文件的路径。第三个是校准文件的路径。第四个是图像的路径。
        // The first is the path to the vocabulary file. The second is the path to the settings file. The third is the path to the calibration files. The fourth is the path to the images. In our example, we could run MultiCol-SLAM
      },
      trajSetForm: {
        dataFormat: "tum",
        plotMode: "xz",
        computeAPE: false,
      },
      trajCompareName: "轨迹对比配置",
      formLabelWidth: "120px",
      trajFormLabelWidth: "180px",
      history: true,
      tableData: [],
      trajName: '',
      trajText: '',
      filePath: '',
      userInfo: '',
      multipleSelection: [], // 存储表格选中项
      trajCompResult: null, // 轨迹对比结果
      showTrajResultName: "轨迹对比结果",
    };
  },
  methods: {
    // 启动SLAM系统
    launch() {
      this.$forceUpdate(); // 强制更新 UI

      // 重新建立 websocket 连接
      console.log("~~~~~~~~~~~~~~", this.websocket);
      
      if (!this.websocket) {

        this.websocket = new WebSocket("ws://127.0.0.1:8765");
      
        this.websocket.onopen = () => {
          console.log("websocket 重新连接成功");
        };

        this.websocket.onmessage = (event) => {
          try {
            const receivedData = JSON.parse(event.data);
            if (Array.isArray(receivedData)) {
              this.$set(this, 'images', receivedData.map(imgBase64 => `data:image/jpeg;base64,${imgBase64}`));
              this.$forceUpdate();
            }
          } catch (error) {
            console.error("接收的 websocket 数据无法解析", error);
          }
        };

        this.websocket.onclose = () => {
          console.log("websocket connection closed");
        };
      } else {
        // this.error_action();
      }
      
      // this.$forceUpdate(); // 强制更新 UI
      // window.location.reload();
      const params = {
        vocabulary: this.form.vocabulary,
        settings: this.form.settings,
        calibration: this.form.calibration + '/',
        images: this.form.images
      };
      window.electronAPI.startSLAM(params);
      this.$forceUpdate(); // 强制更新 UI
      window.location.reload();     

      

    },
    // 停止SLAM系统
    async stop() {
      try {
        const result = await window.electronAPI.stopSLAM();
        this.images = [];
        if (result === 'success') {
          this.$message.success("SLAM系统已停止");
        }

        // window.location.reload();
      } catch (error) {
        this.$message.error("SLAM系统未启动，无法停止");
      }
      
    },
    setConfig() {
      this.dialogFormVisible = true;
    },
    submitForm() {
      this.dialogFormVisible = false;
    },
    // 退出登录
    handleCommand(command) {
      this.$router.push("/"); 
    },
    // 查看历史轨迹数据
    async lookHistory() {
      this.history = false;
      // 从数据库获取轨迹数据
      const trajInfo = await window.electronAPI.getTrajInfo();
      // console.log("Traj Info:", trajInfo);
      this.tableData = trajInfo;

    },
    // 查看某一条轨迹数据
    async handleCheck(row) {
      this.trajName = row.name;
      const dir = '/home/zc/slam/code/my-proj/slam/result/';
      const filePath = dir + row.name;
      const result = await window.electronAPI.readTrajFile(filePath);
      this.trajText = result;
      this.trajVisible = true;
    },
    // 下载某一条轨迹数据
    async handleDownload(row) {
      this.trajName = row.name;
      const dir = '/home/zc/slam/code/my-proj/slam/result/';
      const filePath = dir + row.name;
      const result = await window.electronAPI.readTrajFile(filePath);
      // console.log("Traj Text:", result);
      this.trajText = result;

      const content = this.trajText;

      // 创建一个 Blob 对象
      const blob = new Blob([content], { type: 'text/plain' });
      // 创建一个指向该 Blob 的 URL
      const url = URL.createObjectURL(blob);

      // 创建并点击下载链接
      const a = document.createElement('a');
      a.href = url;
      a.download = row.name;
      document.body.appendChild(a);
      a.click();

      // 在下载后释放 Blob URL
      setTimeout(() => {
        URL.revokeObjectURL(url);
        document.body.removeChild(a);
      }, 0);

    },
    // 删除某一条轨迹数据
    async handleDelete(row) {
      const gTime = row.gTime;
      const name = row.name;
      const result = await window.electronAPI.deleteTrajFile(name, gTime);
      console.log(result);
      
      this.tableData = result;
    },
    // 查看轨迹图
    async handleTraj(row) {
      const dir = '/home/zc/slam/code/my-proj/slam/result/';
      const filePath = dir + row.name;

      try {
        await window.electronAPI.openTrajWindow(filePath);
      } catch (error) {
        console.error("打开轨迹窗口失败", error);
      }
    },
    // 比较轨迹数据
    openTrajSetWindow() {
      if (this.multipleSelection.length < 2) {
        this.$message({
          message: '请选择至少两条轨迹数据！',
          type: 'warning'
        });
      } else {
        this.trajCompareVisible = true;
      }
    },
    // 提交比较轨迹数据表单
    async handleCompareTraj() {
      this.trajCompareVisible = false;
      // 获取选择的轨迹文件路径数组并拼接为字符串，空格分隔
      const dir = '/home/zc/slam/code/my-proj/slam/result/';
      const pathArr = this.multipleSelection.map(item => dir + item.name).join(' ');
      
      try {
        const result = await window.electronAPI.compareTraj(pathArr, this.trajSetForm);

        this.showTrajResult = true;

        this.trajCompResult = result;
        this.trajCompResult.plotImagePath = this.trajCompResult.plotImagePath != null ? require(`@/assets/${this.trajCompResult.plotImagePath}`) : null;
        this.trajCompResult.apePlotImagePath = this.trajSetForm.computeAPE ? require(`@/assets/${this.trajCompResult.apePlotImagePath}`) : null;
        

      } catch (error) {
        this.$message.error("比较轨迹数据失败", error);
      }
    },
    // 选择文件
    async selectFile(item, num) {
      if (item === 1) {
        const path = await window.electronAPI.openFileDialog();
        if (path) {
          if (num === 1) {
            this.form.vocabulary = path;
          } else if (num === 2) {
            this.form.settings = path;
          }
        }
      } else {
        const path = await window.electronAPI.openFilesDialog();
        if (path) {
          if (num === 3) {
            this.form.calibration = path;
          } else if (num === 4) {
            this.form.images = path;
          }
        }
      }
    },

    // 获取用户信息
    async getUserInfo() {
      const user = await window.electronAPI.getUserInfo();
      console.log("User Info:", user);
      this.userInfo = user[0].name;
    },
    // 多选
    handleSelectionChange(val) {
      this.multipleSelection = val;

    },

  },
  created() {
    
  },
  mounted() {
    // 建立 websocket 连接
    this.websocket = new WebSocket("ws://127.0.0.1:8765");

    this.websocket.onopen = () => {
      console.log("websocket 连接成功");
    };

    // 接收图像数据
    this.websocket.onmessage = (event) => {
      try {
        const receivedData = JSON.parse(event.data); // 解析 JSON 数据

        // 如果数据格式正确，且是一个数组
        if (Array.isArray(receivedData)) {
          // this.images = receivedData.map(
          //   (imgBase64) => `data:image/jpeg;base64,${imgBase64}`
          // );

          this.$set(this, 'images', receivedData.map(imgBase64 => `data:image/jpeg;base64,${imgBase64}`));
          this.$forceUpdate(); // 强制更新 UI
        }
      } catch (error) {
        console.error("接收的 websocket 数据无法解析", error);
      }
    };

    // 处理连接关闭
    this.websocket.onclose = () => {
      console.log("websocket connection closed...");
      // this.websocket = null; // 重置 websocket 连接对象
      // 在这里重新建立新的 websocket 连接
      // this.websocket = new WebSocket("ws://127.0.0.1:8765");
     

    };

    // 获取用户信息
    this.getUserInfo();


  },
  beforeDestroy() {
    // // 在组件销毁前关闭 websocket
    // if (this.websocket) {
    //   this.websocket.close();
    // }
  },
};
</script>

<style scoped>
html {
  width: 100vw;
  margin: 0;
  padding: 0;
  height: 100vh;
}

#detail {
  height: 100%;
  width: 100%;
  /* display: flex; */
  /* flex-direction: column; */
  /* flex: 1; */
}

.box {
  height: 100%;
  width: 100%;
  display: flex;
}

.container {
  flex: 1;
  display: flex;
  height: 100%;
  width: 100%;
}

.headArea {
  height: 66px;
  line-height: 0px;
  font-size: 24px;
  font-family: 'Courier New', Courier, monospace;
  box-shadow: 0 15px 15px #f0f2f5;
  z-index: 9999;
  display: flex;
  justify-content: space-between;
  align-items:center;
}

.menu {
  background-color: #f0f2f5;
  padding: 16px 0;
  max-width: 160px;
  display: flex;
  flex-direction: column;
  align-items: baseline;
  padding-top: 30px;
}

.menu .btn {
  margin-bottom: 16px;
  width: 100px;
  box-sizing: content-box;
  text-align: center;
}

.mainArea {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
}

.imgBox {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  width: 360px;
  height: auto;
}

.fileList {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.trajCompareBtn {
  align-self: end;
  margin: 12px 0;
}

.el-dropdown-link {
  cursor: pointer;
  color: #409EFF;
}

.el-icon-arrow-down {
  font-size: 12px;
}

.warning-message {
  z-index: 99999999;
}
</style>
